<?php

/**
 * 通联H5收银台接口服务中心
 * @author chenshubo
 */
namespace App\Service\VendorClient\AlinpayApi;

use App\Service\BaseService;
use App\Service\Code\Normal;
use Service\AppService\IdService;
use App\Service\TaskServer\TaskWorker;

class AlinpayApiClient extends BaseService
{ 
    protected $flag = true;
    private $config = [];
    public function __construct()
    {
        $this->config = config('api.alinpay');
    }
    
    /**
     * 订单提交支付页面收银台
     * @param array $param
     * @return mixed
     */
    public function OrderPay( $param = [] )
    {
        if( empty($param['trxamt']) ) {
            return Normal::returnCode(Normal::FAIL,"trxamt订单金额不能为空");
        }
        
        if( empty($param['reqsn']) ) {
            return Normal::returnCode(Normal::FAIL,"reqsn订单号不能为空");
        }
        
        if( empty($param['returl']) ) {
            return Normal::returnCode(Normal::FAIL,"returl不能为空");
        }
        
        if( empty($param['notify_url']) ) {
            return Normal::returnCode(Normal::FAIL,"notify_url不能为空");
        }
        
        if( empty($param['remark']) ) {
            return Normal::returnCode(Normal::FAIL,"remark不能为空");
        }
        
        $orderSn = date('YmdHis');
        $alinPayData = [
            'trxamt'        => bcmul($param['trxamt'], 100),//单位为分
            'reqsn'         => $param['reqsn'],
            'returl'        => $param['returl'],
            'notify_url'    => $param['notify_url'],
            'body'          => '收银付款',
            'remark'        => $param['remark'],
            'validtime'     => $this->config['validtime'],
        ];
        $res = $this->getSign($alinPayData);
        if( $res['code'] != Normal::SUC ) {
            return $res;
        }
        
        return Normal::returnCode(Normal::SUC,"获取成功",$this->changeToAlinPayWeb($res['data']));
    }    
    
    /**
     * 订单取消接口
     * 只能撤销当天的交易，全额退款，实时返回退款结果
     * @param array $params
     * @return mixed
     */
    public function OrderCancel( $param = [] )
    {
        if( empty($param['trxamt']) ) {
            return Normal::returnCode(Normal::FAIL,"trxamt订单金额不能为空");
        }
        
        if( empty($param['oldreqsn']) ) {
            return Normal::returnCode(Normal::FAIL,"oldreqsn订单号不能为空");
        }
        
        $alinPayData = [
            'trxamt'        => bcmul($param['trxamt'], 100),//单位为分
            'reqsn'         => DockeyId::instance()->getIncrementId(),//商户唯一订单
            'oldreqsn'      => $param['oldreqsn'],//原交易的商户交易单号
        ];
        
        $this->flag = false;
        $res = $this->getSign($alinPayData);
        if( $res['code'] != Normal::SUC ) {
            return $res;
        }
        
        $requestParam = [
            'method'    => 'cancelOrder',
            'data'      => $res['data'],
        ];
        return $this->execute($requestParam);
    }
    
    /**
     * 订单退款接口
     * 支持部分金额退款
     * @param array $params
     * @return mixed
     */
    public function OrderRefund( $param = [] )
    {
        if( empty($param['money']) ) {
            return Normal::returnCode(Normal::FAIL,"money订单金额不能为空");
        }
        
        if( empty($param['order_sn']) ) {
            return Normal::returnCode(Normal::FAIL,"order_sn订单号不能为空");
        }
        
        $pay_time = $param['pay_time'];
        $reqsn    = IdService::instance()->getOtherID();
        $order_sn = $param['order_sn'];
        $trxamt   = bcmul($param['money'], 100);
        
        //当日退款用撤销，超期用退款接口
        if( date("Ymd") > date("Ymd",$pay_time) ) {
            $alinPayData = [
                'trxamt'        => $trxamt,//单位为分
                'reqsn'         => $reqsn,
                'oldreqsn'      => $order_sn,//原交易的商户交易单号
                'remark'        => '线上退款',//备注信息
            ];
            $this->flag = false;
            $res = $this->getSign($alinPayData);
            if( $res['code'] != Normal::SUC ) {
                return $res;
            }
            $requestParam = [
                'method'    => 'refundOrder',
                'data'      => $res['data'],
            ];
        } else {
            $alinPayData = [
                'trxamt'        => $trxamt,//单位为分
                'reqsn'         => $reqsn,//商户唯一订单
                'oldreqsn'      => $order_sn,//原交易的商户交易单号
            ];
            
            $this->flag = false;
            $res = $this->getSign($alinPayData);
            if( $res['code'] != Normal::SUC ) {
                return $res;
            }
            $requestParam = [
                'method'    => 'cancelOrder',
                'data'      => $res['data'],
            ];
        }
        return $this->execute($requestParam);
    }
    
    /**
     * 交易查询
     * @param array $params
     * @return mixed
     */
    public function OrderQuery( $param = [] )
    {
        if( empty($param['pay_sn']) ) {
            return Normal::returnCode(Normal::FAIL,"pay_sn支付流水号不能为空");
        }
    
        $alinPayData = [
            'trxid' => $param['pay_sn'],//支付的收银宝平台流水
        ];
    
        $this->flag = false;
        $res = $this->getSign($alinPayData);
        if( $res['code'] != Normal::SUC ) {
            return $res;
        }
        
        $requestParam = [
            'method'    => 'queryOrder',
            'data'      => $res['data'],
        ];
        return $this->execute($requestParam);
    }
    
    /**
     * 跳转通联支付页面进行支付
     * @param array $param
     */
    public function changeToAlinPayWeb( $param = [] ) 
    {
        $alinpayConfig = $this->config;
        $inputValue = '';
        foreach ($param as $key => $value ) {
            $inputValue .= "<input type='hidden' name='{$key}' id='{$key}' value='{$value}' />";
        }
        return <<<EOF
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
</head>
<body onload="document.getElementById('from1').submit()">
    <form method="post" id="from1" action="{$alinpayConfig['acount']['h5Pay']}">
      {$inputValue}
    </form>
</body>
</html>
EOF;
    }
    
    /**
     * 验证回调签名
     */
    public function checkSign( $param = [] )
    {
        $sign = $param['sign'];
        unset($param['sign']);
        ksort($param);
        $bufSignSrc = $this->ToUrlParams($param);
        $public_key = $this->config['public_key'];
        $public_key = chunk_split($public_key , 64, "\n");
        $key = "-----BEGIN PUBLIC KEY-----\n$public_key-----END PUBLIC KEY-----\n";
        $result= openssl_verify($bufSignSrc,base64_decode($sign), $key );
        return $result;
    }
    
    /**
     * 获取签名参数
     * @param array $param
     */
    protected function getSign( $param = [] )
    {
        $parameter = array_merge($param,$this->getCommonParameters());
        ksort($parameter);
        
        //组装签名字符串
        $bufSignSrc = self::ToUrlParams($parameter);
        //进行RSA签名
        $private_key = chunk_split($this->config['private_key'] , 64, "\n");
        $key = "-----BEGIN RSA PRIVATE KEY-----\n".wordwrap($private_key)."-----END RSA PRIVATE KEY-----";
        $signRes = openssl_sign($bufSignSrc, $signature, $key );
        if( !$signRes ) {
            return Normal::returnCode(Normal::FAIL,"签名失败");
        }
        
        //加密后的内容通常含有特殊字符，需要编码转换下，在网络间通过url传输时要注意base64编码是否是url安全的
        $parameter['sign'] = base64_encode($signature);
        return Normal::returnCode(Normal::SUC,"签名成功",$parameter);
    }
    
    /**
     * 处理参数签名
     * @param array $array
     * @return string
     */
    public static function ToUrlParams(array $array)
    {
        $buff = "";
        foreach ($array as $k => $v)
        {
            if($v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }
        
        $buff = trim($buff, "&");
        return $buff;
    }
    
    /**
     * 获取公共参数配置
     */
    protected function getCommonParameters( $alinpayConfig = [] )
    {
        $param = [
            'cusid'         => $this->config['cusid'],//实际交易商户号
            'appid'         => $this->config['appid'],//平台分配的APPID
            'version'       => $this->config['version'],
            'signtype'      => $this->config['signtype'],//签名类型
            'randomstr'     => '1792e8a34327b8c4bbc3235d550dae90',//md5(microtime(true) . mt_rand(1000,9999)),//随机字符串
        ];
        
        if( $this->flag ) {
            $param['charset'] = $this->config['charset'];//商户网站使用的编码格式，支持UTF-8、GBK跟商户网站的编码一致
        }
        
        return $param;
    }
    
    /**
     * CURL请求
     * @param $params
     * @return mixed
     */
    public function execute( $param = [] )  
    {
        $method     = $param['method'];
        $data       = $param['data'];
        $url        = $this->config['acount'][$param['method']];
        $parameter = [
            [
                'tag'       => 'Alinpay_'.$param['method'],
                'uid'       => IdService::instance()->getOtherID(),
                'url'       => $url,
                'data'      => $data,
                'method'    => 'post',
                'format'    => 'x-www-form-urlencoded',
                'header'    => [],
            ],
        ];
        $resTask    = TaskWorker::instance()->addTask($parameter);
        $resTask    = current($resTask);
        $response   = @json_decode($resTask['response']['result'],true);        
        $returnMsg  = [];
        if( isset($response['retcode']) && $response['retcode'] == 'SUCCESS' ) {
            if( $response['trxstatus'] == '0000' ) {
                $returnMsg = Normal::returnCode(Normal::SUC,"交易成功",$response);
            } else {
                $returnMsg = Normal::returnCode(Normal::FAIL,$response['errmsg']);
            }
        } else if( isset($response['retcode']) ) {
            $returnMsg = Normal::returnCode(Normal::FAIL,$response['retmsg']);
        } else {
            $returnMsg = Normal::returnCode(Normal::FAIL,"请求失败");
        }
        return $returnMsg;
    }
}
